// 启动时指定文件名，当有新连接时就把文件内容完整的发给对方。
// 版本一，一次性把文件读入内存，一次性调用 send 发送完毕。
// 该版本有个明显的缺陷，服务端内存消耗与（并发连接数 * 文件大小）成正比，
// 文件越大，内存消耗越多。
#include <muduo/base/Logging.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/TcpServer.h>
#include <stdio.h>

using namespace muduo;
using namespace muduo::net;

const char *g_file = NULL;

string
ReadFile(const char *filename) {
	string content;
	FILE *fp = ::fopen(filename, "rb");
	if (fp) {
		const int kBufSize = 1024 * 1024;
		char ioBuf[kBufSize];
		::setbuffer(fp, ioBuf, sizeof(ioBuf));

		char buf[kBufSize];
		size_t nread = 0;
		while ((nread = ::fread(buf, 1, sizeof(buf), fp)) > 0)
			content.append(buf, nread);
		::fclose(fp);
	}
	return content;
}

void
OnHighWaterMark(const TcpConnectionPtr &conn, size_t len) {
	LOG_INFO << "HighWaterMark " << len;
}

void OnConnection(const TcpConnectionPtr &conn) {
	LOG_INFO << "FileServer - " << conn->peerAddress().toIpPort() << " -> "
	         << conn->localAddress().toIpPort() << " is "
	         << (conn->connected() ? "UP" : "DOWN");
	if (conn->connected()) {
		LOG_INFO << "FileServer - Sending file " << g_file
		         << " to " << conn->peerAddress().toIpPort();
		conn->setHighWaterMarkCallback(OnHighWaterMark, 64 * 1024);
		// 考虑到文件可能被其他程序修改，每次建立连接时都打开文件，
		// 如果文件是不可被其他程序修改，则可以所有连接共享此文件 fd ，每次通过 pread 读取。
		string fileContent = ReadFile(g_file);
		conn->send(fileContent);
		conn->shutdown();
		LOG_INFO << "FileServer - done";
	}
}

// 可以使用 nc ip port > file 进行测试下载。
int 
main(int argc, char **argv) {
	LOG_INFO << "pid = " << getpid();
	if (argc > 1) {
		g_file = argv[1];

		EventLoop loop;
		InetAddress listenAddr(2021);
		TcpServer server(&loop, listenAddr, "FileServer");
		server.setConnectionCallback(OnConnection);
		server.start();
		loop.loop();
	} else {
		fprintf(stderr, "Usage %s file_for_downloading\n", argv[0]);
	}
}
